對於絕大多數後端工程師而言,MVC(Model-View-Controller)是我們學習 Web 開發時接觸的第一個,也是最經典的架構模式。無論是 Ruby on Rails, Django, Laravel 還是 Spring MVC,都深深地烙印著 MVC 的思想。
MVC 是一個偉大且成功的模式,但它主要誕生於一個以「伺服器端渲染頁面」為主的時代。當我們的應用程式演變為提供純粹 API 的服務、微服務時,我們需要反思:MVC 還是最佳選擇嗎?隨著需求的多元化與系統規模的擴大,傳統 MVC 架構在維護性、可測試性與彈性上逐漸暴露出限制。
在經典的 MVC 模式中,三個元件的職責是:
在我們的 API 服務中,「View」的角色被簡化為 JSON 回應。這種簡化雖然讓開發流程更直接,但也讓 MVC 的分層意義變得模糊,特別是在缺乏複雜 UI 的情境下。
隨著業務變複雜,許多遵循 MVC 模式的專案會不自覺地陷入以下困境:
臃腫的控制器 (Fat Controllers):大量的業務邏輯,因為無處安放,最終都被塞進了 Controller 的方法中。Controller 不僅要處理 HTTP 請求和回應,還要負責資料驗證、流程編排、呼叫多個 Model。這使得 Controller 變得極其臃腫,難以測試和維護。當 Controller 變得過於龐大時,任何小的需求變動都可能牽一髮動全身,導致維護成本大幅提升。
貧血的模型 (Anemic Models):與此同時,Model 逐漸退化成一個只有 Getters/Setters 的「資料袋」,所有的業務邏輯都被抽離到 Controller 或某些 Service
層中,完全違背了物件導向中「資料與操作相結合」的初衷。這種設計讓 Model 失去了主動性,導致業務規則分散在各處,降低了程式碼的可讀性與一致性。
以資料庫為中心:在許多 MVC 框架中(特別是 ActiveRecord 風格的),Model 與資料庫的表結構是緊緊綁定的。這導致整個應用的設計都圍繞著資料庫 schema 展開,業務邏輯隱性地依賴於資料庫的實作細節。這使得「脫離資料庫來測試業務邏輯」變得異常困難。當我們想要替換資料庫或進行單元測試時,往往會發現業務邏輯與資料存取層糾纏不清。
我們的專案所採用的六角形架構,正是為了解決上述問題而設計的。
針對臃腫的控制器:我們的 Controller
極其輕薄。它的唯一職責是「翻譯」HTTP 請求,然後委派給 Usecase
。所有的業務流程都在 Usecase
中,職責清晰。這樣的設計讓 Controller 易於維護與測試,並且能專注於協調請求與回應。
針對貧血的模型:我們的 Domain
層是「充血」的。User
物件不僅有資料,更有保護其內部一致性的驗證邏輯和業務方法。它是應用程式中真正「聰明」的部分。這種設計讓業務規則集中於 Domain 物件,提升了程式碼的可讀性、可維護性與一致性。
針對以資料庫為中心:這是兩者最本質的區別。在我們的架構中,業務領域(Domain)才是宇宙的中心,而不是資料庫。資料庫只是一個可以被隨時替換的「基礎設施」,是一個被動實現了 Repository
介面的「Adapter」。依賴倒轉原則確保了我們的核心業務邏輯對 GORM、對 MySQL 一無所知。這讓我們能夠輕鬆地替換資料存取技術,或在測試時使用記憶體資料庫模擬,極大提升了系統的彈性與可測試性。
從 MVC 到六角形架構,最核心的是思維模式的轉變:
我們不再問「這個邏輯該放在 MVC 的哪一層?」,而是問「這個邏輯是核心業務,還是與外部技術互動的細節?」。如果是前者,它就屬於「內部」;如果是後者,它就是一個需要被隔離在外的「Adapter」。這種思維方式讓我們在設計系統時,能夠更聚焦於業務本身,並且更容易因應技術棧的變動。
MVC 是一個優秀的模式,對於快速開發簡單的、以 UI 為中心的應用程式,它依然非常高效。然而,當我們需要建置一個複雜的、需要長期維護的、且可能需要適應不同技術(例如,未來可能需要支援 gRPC 或命令列觸發)的後端服務時,六角形架構所提供的高可測試性、高靈活性和邊界清晰性,展現出了無與倫比的優勢。
我們專案至今所享受到的所有好處——輕鬆的單元測試、清晰的模組劃分——都源於我們在專案之初,就選擇了這條「以業務為核心,與技術細節解耦」的道路。六角形架構不僅提升了開發效率,更讓我們能夠從容應對未來的技術挑戰與業務變革,真正實現了「穩定核心、彈性外圍」的理想架構。